/** 
 *@msg 错误采集思路
 * window.onerror：捕获常规错误、异步错误，但不能捕获资源错误 
 * window.addEventListener('error',：静态资源加载失败时
 * window.addEventListener('unhandledrejection '：Promise中抛出的错误
 * Vue.config.errorHander：vue错误
 * ErrorBoundary 错误边界的功能：react错误
 * 针对浏览器内置的 XMLHttpRequest、fetch 对象，利用 AOP 切片编程重写该方法，实现对请求的接口拦截，从而获取接口报错的情况并上报
 */

import { EVENTTYPES, HTTPTYPE } from '../../common/constant'
import { options } from '../options'
import { throttle, notify } from './tools'
// 上一个页面地址  --->下一个页面地址
let lastHref = document.location.href
// 重写事件
class RewriteEvents {
    // 1. 捕获错误
    listenError() {
        window.addEventListener('error', function (e) {
            notify(EVENTTYPES.ERROR, e);
        }, true)
    }
    // 2. 监听click事件
    domReplace() {
        // 节流，默认0s
        const clickThrottle = throttle(notify, options.throttleDelayTime);
        document.addEventListener('click', function (this) {
            clickThrottle(EVENTTYPES.CLICK, {
                category: 'click',
                data: this,
            });
        }, true)
    }
    // 3.监听hashchange
    listenHashchange() {
        if ('onhashchange' in window) {
            window.addEventListener('hashchange', function (e) {
                // e可以分为前oldValue,后newValue两个状态
                notify(EVENTTYPES.HASHCHANGE, e)
            }, true)
        }
    }
    // 3.监听 historyReplace
    historyReplace() {
        if ('history' in window) {
            if (window.history.pushState && window.history.replaceState) {
                /* 
                处理 window 对象上的 popstate
                window.onpopstate = function(event) {
                  alert("location: " + document.location + ", state: " + JSON.stringify(event.state));
                };
                
                history.pushState({page: 1}, "title 1", "?page=1");
                history.pushState({page: 2}, "title 2", "?page=2");
                history.replaceState({page: 3}, "title 3", "?page=3");
                history.back(); // 弹出 "location: http://example.com/example.html?page=1, state: {"page":1}"
                history.back(); // 弹出 "location: http://example.com/example.html, state: null
                history.go(2);  // 弹出 "location: http://example.com/example.html?page=3, state: {"page":3}
                */
                let oldOnpopstate = window.onpopstate
                window.onpopstate = function (this, args) {
                    const to = document.location.href
                    const from = lastHref;
                    lastHref = to;
                    notify(EVENTTYPES.HISTORY, {
                        from,
                        to,
                    });
                    oldOnpopstate && oldOnpopstate.apply(this, args);
                };
                const handlerPush = window.history.pushState
                window.history.pushState = function (args) {
                    const url = args.length > 2 ? args[2] : ''
                    if (url) {
                        const from = lastHref;
                        const to = String(url);
                        lastHref = to;
                        notify(EVENTTYPES.HISTORY, {
                            from,
                            to,
                        });
                    }
                    return handlerPush.apply(this, args);
                }
                const handlerReplace = window.history.replaceState
                window.history.replaceState = function (args) {
                    const url = args.length > 2 ? args[2] : ''
                    if (url) {
                        const from = lastHref;
                        const to = String(url);
                        lastHref = to;
                        notify(EVENTTYPES.HISTORY, {
                            from,
                            to,
                        });
                    }
                    return handlerReplace.apply(this, args);
                }
            }
        }
    }
    // 4. 监听xhr
    xhrReplace() {
        if (!('XMLHttpRequest' in window)) {
            return;
        }

        const originalXhrProto = XMLHttpRequest.prototype;
        const handlerOpen = originalXhrProto.open
        // 重写open方法
        originalXhrProto.open = function (...args) {
            console.log(args, '---***');
            // method, url, async, user, pass
            this.websee_xhr = {
                method: args[0],
                url: args[1],
                sTime: Date.now(),
                type: HTTPTYPE.XHR,
            };
            handlerOpen.call(this, ...args);
        };


        const handlerSend = originalXhrProto.send
        // XMLHttpRequest.onloadend：loadend 事件（请求完成，不管成功或失败）的监听函数
        // 重写send方法
        originalXhrProto.send = function (this, ...args) {
            const { method, url } = this.websee_xhr;

            handlerSend.apply(this, args);

            this.addEventListener('loadend', function (e) {
                console.log(e, 'loadend___');
                const { responseType, response, status } = e;
                this.websee_xhr.requestData = args[0];
                const eTime = Date.now();
                // 设置该接口的time，用户用户行为按时间排序
                this.websee_xhr.time = this.websee_xhr.sTime;
                this.websee_xhr.Status = status;
                // 用户设置handleHttpStatus函数来判断接口是否正确，只有接口报错时才保留response
                if (options.handleHttpStatus && typeof options.handleHttpStatus == 'function') {
                    this.websee_xhr.response = response && JSON.parse(response);
                }
                // 接口的执行时长
                this.websee_xhr.elapsedTime = eTime - this.websee_xhr.sTime;
                // 执行之前注册的xhr回调函数
                notify(EVENTTYPES.XHR, this.websee_xhr);
            })
        }
    }
    // 5.监听fetch
    fetchReplace() {
        if (!('fetch' in window)) {
            return;
        }
        let handler = window.fetch
        window.fetch = function (url, config) {
            let sTime = Date.now()

            let fetchData = {
                type: HTTPTYPE.FETCH,
                method: (config && config.method) || 'GET',
                requestData: config && config.body,
                url,
                response: '',
            };

            // 获取配置的headers
            // new Headers

            const headers = new Headers(config && config.headers || {});
            Object.assign(headers, {
                setRequestHeader: headers.set,
            });
            config = Object.assign({}, config, headers);
            return handler.apply(window, [url, config]).then((res) => {
                // 克隆一份，防止被标记已消费
                const tempRes = res.clone();
                const eTime = Date.now();
                fetchData = Object.assign({}, fetchData, {
                    elapsedTime: eTime - sTime,
                    Status: tempRes.status,
                    time: sTime,
                });

                tempRes.text().then((data) => {
                    // 用户设置handleHttpStatus函数来判断接口是否正确，只有接口报错时才保留response
                    if (options.handleHttpStatus && typeof options.handleHttpStatus == 'function') {
                        fetchData.response = data;
                    }
                    notify(EVENTTYPES.FETCH, fetchData);
                });
                return res;
            },
                // 接口报错
                (err) => {
                    const eTime = Date.now()

                    fetchData = Object.assign({}, fetchData, {
                        elapsedTime: eTime - sTime,
                        status: 0,
                        time: sTime,
                    });
                    notify(EVENTTYPES.FETCH, fetchData);
                    throw err;
                })
        }
    }
    //6. 监听promise
    unhandledrejectionReplace() {
        window.addEventListener('unhandledrejection', function (ev) {
            notify(EVENTTYPES.UNHANDLEDREJECTION, ev)
        })
    }
    // 白屏时长
    whiteScreen() {
        notify(EVENTTYPES.WHITESCREEN, '')
    }
}
const rewriteEvents = new RewriteEvents()
export { rewriteEvents }